/*
 * PCF8574 GPIO Port Expander for ESP32
 * Based on Arduino PCF8574 library by Renzo Mischianti
 * Adapted for ESP-IDF with custom I2cMaster class
 *
 * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
 * SPDX-License-Identifier: Apache-2.0
 */

#include "PCF8574.h"
#include "esp_log.h"
#include "esp_check.h"
#include "driver/gpio.h"
#include <cstring>

// 添加缺失的宏定义
#ifndef CONFIG_LOG_MAXIMUM_LEVEL
#define CONFIG_LOG_MAXIMUM_LEVEL 3
#endif

#ifndef CONFIG_FREERTOS_HZ
#define CONFIG_FREERTOS_HZ 1000
#endif

static const char* TAG = "PCF8574";

// PCF8574地址范围
#define PCF8574_MIN_ADDRESS 0x20
#define PCF8574_MAX_ADDRESS 0x27
#define PCF8574A_MIN_ADDRESS 0x38
#define PCF8574A_MAX_ADDRESS 0x3F

// 默认读取延迟
#define DEFAULT_LATENCY_MS 10

// PCF8574 实现
PCF8574::PCF8574(std::shared_ptr<I2cMaster> i2c, uint8_t address, uint32_t scl_speed_hz) {
    _i2c = i2c;
    _address = address;
    _sclSpeedHz = scl_speed_hz;
    _usingInterrupt = false;
    _deviceHandle = nullptr;
    
    ESP_LOGI(TAG, "PCF8574创建完成，地址: 0x%02X", _address);
}

PCF8574::PCF8574(std::shared_ptr<I2cMaster> i2c, uint8_t address, uint8_t interruptPin, 
                 InterruptCallback interruptFunction, uint32_t scl_speed_hz) {
    _i2c = i2c;
    _address = address;
    _sclSpeedHz = scl_speed_hz;
    _usingInterrupt = true;
    _interruptPin = interruptPin;
    _interruptFunction = interruptFunction;
    _deviceHandle = nullptr;
    
    ESP_LOGI(TAG, "PCF8574创建完成（中断模式），地址: 0x%02X，中断引脚: %d", _address, _interruptPin);
}

PCF8574::~PCF8574() {
    if (_usingInterrupt) {
        detachInterrupt();
    }
    
    // 移除设备句柄
    if (_deviceHandle && _i2c) {
        _i2c->removeDevice(_deviceHandle);
        _deviceHandle = nullptr;
    }
    
    ESP_LOGI(TAG, "PCF8574销毁完成，地址: 0x%02X", _address);
}

esp_err_t PCF8574::begin() {
    ESP_LOGI(TAG, "开始初始化PCF8574，地址: 0x%02X", _address);
    
    if (!isValidAddress(_address)) {
        ESP_LOGE(TAG, "无效的PCF8574地址: 0x%02X", _address);
        transmissionStatus = 4; // 地址错误
        return ESP_ERR_INVALID_ARG;
    }
    
    if (!_i2c || !_i2c->isInitialized()) {
        ESP_LOGE(TAG, "I2C未初始化");
        transmissionStatus = 4;
        return ESP_ERR_INVALID_STATE;
    }
    
    // 检查设备是否在线
    if (!_i2c->probeDevice(_address)) {
        ESP_LOGE(TAG, "PCF8574设备不在线，地址: 0x%02X", _address);
        transmissionStatus = 2; // 设备不响应
        return ESP_ERR_NOT_FOUND;
    }
    
    // 添加设备句柄以优化性能
    _deviceHandle = _i2c->addDevice(_address, _sclSpeedHz);
    if (!_deviceHandle) {
        ESP_LOGE(TAG, "添加PCF8574设备句柄失败，地址: 0x%02X", _address);
        transmissionStatus = 4;
        return ESP_ERR_INVALID_STATE;
    }
    
    // 如果有引脚需要设置，进行初始化
    if (writeMode > 0 || readMode > 0) {
        resetInitial = writeModeUp | readModePullUp;//只把需要输入上拉的才设置为高电平
        //resetInitial = writeModeUp | readMode; //不管输入需不需要上拉都统一把输入设置为高电平
        initialBuffer = writeModeUp | readModePullUp;
        byteBuffered = initialBuffer;
        writeByteBuffered = writeModeUp;
        
        esp_err_t ret = _i2c->transmit(_deviceHandle, &resetInitial, 1);
        if (ret != ESP_OK) {
            ESP_LOGE(TAG, "初始化PCF8574失败: %s", _i2c->errorToString(ret));
            transmissionStatus = 1; // 传输失败
            return ret;
        }
    }
    
    // 设置中断
    if (_usingInterrupt) {
        attachInterrupt();
    }
    
    // 初始化时间
    lastReadMillis = millis();
    transmissionStatus = 0; // 成功
    
    ESP_LOGI(TAG, "PCF8574初始化成功，地址: 0x%02X", _address);
    
    return ESP_OK;
}

void PCF8574::pinMode(uint8_t pin, uint8_t mode, uint8_t output_start) {
    if (pin > 7) {
        ESP_LOGE(TAG, "无效引脚号: %d", pin);
        return;
    }
    
    uint8_t pinBit = bit(pin);
    uint8_t oldWriteMode = writeMode;
    uint8_t oldReadMode = readMode;
    
    switch (mode) {
        case OUTPUT:
            writeMode |= pinBit;
            if (output_start == HIGH) {
                writeModeUp |= pinBit;
            } else {
                writeModeUp &= ~pinBit;
            }
            
            readMode &= ~pinBit;
            readModePullDown &= ~pinBit;
            readModePullUp &= ~pinBit;
            break;
            
        case INPUT:
            writeMode &= ~pinBit;
            readMode |= pinBit;
            readModePullDown |= pinBit;
            readModePullUp &= ~pinBit;
            break;
            
        case INPUT_PULLUP:
            writeMode &= ~pinBit;
            readMode |= pinBit;
            readModePullDown &= ~pinBit;
            readModePullUp |= pinBit;
            break;
            
        default:
            ESP_LOGE(TAG, "不支持的引脚模式: %d", mode);
            return;
    }
}

esp_err_t PCF8574::digitalWrite(uint8_t pin, uint8_t value) {
    if (pin > 7) {
        ESP_LOGE(TAG, "无效引脚号: %d", pin);
        transmissionStatus = 4;
        return ESP_ERR_INVALID_ARG;
    }
    
    if (!_deviceHandle) {
        ESP_LOGE(TAG, "设备句柄无效，请先调用begin()");
        transmissionStatus = 4;
        return ESP_ERR_INVALID_STATE;
    }

    uint8_t pinBit = bit(pin);
    
    if (value == HIGH) {
        writeByteBuffered |= pinBit;
        byteBuffered |= pinBit;
    } else {
        writeByteBuffered &= ~pinBit;
        byteBuffered &= ~pinBit;
    }
    
    // 计算要写入的字节
    uint8_t writeData = (writeByteBuffered & writeMode) | (resetInitial & readMode);
    
    esp_err_t ret = _i2c->transmit(_deviceHandle, &writeData, 1);
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "写入PCF8574失败: %s", _i2c->errorToString(ret));
        transmissionStatus = 1;
        return ret;
    }
    
    // 更新缓冲区
    byteBuffered = (writeByteBuffered & writeMode) | (initialBuffer & readMode);
    transmissionStatus = 0;
    
    return ESP_OK;
}

uint8_t PCF8574::digitalRead(uint8_t pin, bool forceReadNow) {
    if (pin > 7) {
        ESP_LOGE(TAG, "无效引脚号: %d", pin);
        transmissionStatus = 4;
        return LOW;
    }
    
    if (!_deviceHandle) {
        ESP_LOGE(TAG, "设备句柄无效，请先调用begin()");
        transmissionStatus = 4;
        return LOW;
    }
    
    uint8_t pinBit = bit(pin);
    uint8_t value = (pinBit & readModePullUp) ? HIGH : LOW;
    
    // 检查引脚是否已经设置
    bool pinAlreadySet = ((pinBit & (readModePullDown & byteBuffered)) > 0) ||
                        ((pinBit & (readModePullUp & ~byteBuffered)) > 0);
    
    if (pinAlreadySet) {
        value = (pinBit & byteBuffered) ? HIGH : LOW;
    } else if (forceReadNow || (millis() > lastReadMillis + latency)) {
        readBuffer(forceReadNow);
        value = (pinBit & byteBuffered) ? HIGH : LOW;
    }
    
    // 如果是下拉模式且读到高电平，重置缓冲区
    if ((pinBit & readModePullDown) && value == HIGH) {
        byteBuffered ^= pinBit;
    } else if ((pinBit & readModePullUp) && value == LOW) {
        byteBuffered ^= pinBit;
    } else if (pinBit & writeByteBuffered) {
        value = HIGH;
    }
    
    return value;
}

#ifndef PCF8574_LOW_MEMORY
DigitalInput PCF8574::digitalReadAll(void) {
    readBuffer();
    
    uint8_t byteRead = byteBuffered | writeByteBuffered;
    
    // 设置输入引脚值
    if (bit(0) & readMode) digitalInput.p0 = (byteBuffered & bit(0)) ? HIGH : LOW;
    if (bit(1) & readMode) digitalInput.p1 = (byteBuffered & bit(1)) ? HIGH : LOW;
    if (bit(2) & readMode) digitalInput.p2 = (byteBuffered & bit(2)) ? HIGH : LOW;
    if (bit(3) & readMode) digitalInput.p3 = (byteBuffered & bit(3)) ? HIGH : LOW;
    if (bit(4) & readMode) digitalInput.p4 = (byteBuffered & bit(4)) ? HIGH : LOW;
    if (bit(5) & readMode) digitalInput.p5 = (byteBuffered & bit(5)) ? HIGH : LOW;
    if (bit(6) & readMode) digitalInput.p6 = (byteBuffered & bit(6)) ? HIGH : LOW;
    if (bit(7) & readMode) digitalInput.p7 = (byteBuffered & bit(7)) ? HIGH : LOW;
    
    // 设置输出引脚值
    if (bit(0) & writeMode) digitalInput.p0 = (writeByteBuffered & bit(0)) ? HIGH : LOW;
    if (bit(1) & writeMode) digitalInput.p1 = (writeByteBuffered & bit(1)) ? HIGH : LOW;
    if (bit(2) & writeMode) digitalInput.p2 = (writeByteBuffered & bit(2)) ? HIGH : LOW;
    if (bit(3) & writeMode) digitalInput.p3 = (writeByteBuffered & bit(3)) ? HIGH : LOW;
    if (bit(4) & writeMode) digitalInput.p4 = (writeByteBuffered & bit(4)) ? HIGH : LOW;
    if (bit(5) & writeMode) digitalInput.p5 = (writeByteBuffered & bit(5)) ? HIGH : LOW;
    if (bit(6) & writeMode) digitalInput.p6 = (writeByteBuffered & bit(6)) ? HIGH : LOW;
    if (bit(7) & writeMode) digitalInput.p7 = (writeByteBuffered & bit(7)) ? HIGH : LOW;
    
    // 重置缓冲区
    byteBuffered = (initialBuffer & readMode) | (byteBuffered & ~readMode);
    
    return digitalInput;
}

esp_err_t PCF8574::digitalWriteAll(DigitalInput digitalInput) {
    uint8_t value = 0;
    
    if (digitalInput.p0) value |= bit(0);
    if (digitalInput.p1) value |= bit(1);
    if (digitalInput.p2) value |= bit(2);
    if (digitalInput.p3) value |= bit(3);
    if (digitalInput.p4) value |= bit(4);
    if (digitalInput.p5) value |= bit(5);
    if (digitalInput.p6) value |= bit(6);
    if (digitalInput.p7) value |= bit(7);
    
    return digitalWriteAllBytes(value);
}
#else
uint8_t PCF8574::digitalReadAll(void) {
    readBuffer();
    
    uint8_t value = byteBuffered | writeByteBuffered;
    
    // 重置缓冲区
    byteBuffered = (initialBuffer & readMode) | (byteBuffered & ~readMode);
    
    return value;
}

esp_err_t PCF8574::digitalWriteAll(uint8_t digitalInput) {
    return digitalWriteAllBytes(digitalInput);
}
#endif

esp_err_t PCF8574::digitalWriteAllBytes(uint8_t allpins) {
    if (!_deviceHandle) {
        ESP_LOGE(TAG, "设备句柄无效，请先调用begin()");
        transmissionStatus = 4;
        return ESP_ERR_INVALID_STATE;
    }
    
    writeByteBuffered = allpins;
    
    uint8_t writeData = (writeByteBuffered & writeMode) | (resetInitial & readMode);
    
    esp_err_t ret = _i2c->transmit(_deviceHandle, &writeData, 1);
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "写入所有字节失败: %s", _i2c->errorToString(ret));
        transmissionStatus = 1;
        return ret;
    }
    
    byteBuffered = (writeByteBuffered & writeMode) | (initialBuffer & readMode);
    transmissionStatus = 0;
    
    return ESP_OK;
}

// ==================== 编码器功能 ====================

void PCF8574::encoder(uint8_t pinA, uint8_t pinB) {
    if (pinA > 7 || pinB > 7) {
        ESP_LOGE(TAG, "无效编码器引脚");
        return;
    }
    
    pinMode(pinA, INPUT_PULLUP);
    pinMode(pinB, INPUT_PULLUP);
    
    encoderPins[pinA] = true;
    encoderPins[pinB] = true;
    
    ESP_LOGI(TAG, "编码器配置完成，引脚A: %d, B: %d", pinA, pinB);
}

#ifdef BASIC_ENCODER_ALGORITHM
bool PCF8574::readEncoderValue(uint8_t pinA, uint8_t pinB, volatile long *encoderValue, bool reverseRotation) {
    if (pinA > 7 || pinB > 7 || encoderValue == nullptr) {
        ESP_LOGE(TAG, "无效编码器参数");
        return false;
    }
    
    // 暂时禁用中断
    if (_usingInterrupt) {
        detachInterrupt();
    }
    
    bool changed = false;
    
    uint8_t na = digitalRead(pinA, true);
    uint8_t nb = digitalRead(pinB, true);
    
    uint8_t encoderPinALast = (encoderValues & bit(pinA)) ? HIGH : LOW;
    uint8_t encoderPinBLast = (encoderValues & bit(pinB)) ? HIGH : LOW;
    
    if ((encoderPinALast != na || encoderPinBLast != nb) && 
        (encoderPinALast == LOW) && (na == HIGH)) {
        
        if (nb == LOW) {
            *encoderValue = *encoderValue + (!reverseRotation ? +1 : -1);
            changed = true;
        } else {
            *encoderValue = *encoderValue + (!reverseRotation ? -1 : +1);
            changed = true;
        }
    }
    
    // 更新编码器值
    if (encoderPinALast != na) encoderValues ^= bit(pinA);
    if (encoderPinBLast != nb) encoderValues ^= bit(pinB);
    
    // 重新启用中断
    if (_usingInterrupt) {
        attachInterrupt();
    }
    
    return changed;
}

int8_t PCF8574::readEncoderValue(uint8_t pinA, uint8_t pinB) {
    volatile long encoderValue = 0;
    if (readEncoderValue(pinA, pinB, &encoderValue)) {
        return (int8_t)encoderValue;
    }
    return 0;
}
#endif

// ==================== 中断功能 ====================

void PCF8574::attachInterrupt() {
    if (!_usingInterrupt) {
        ESP_LOGE(TAG, "中断未配置");
        return;
    }
    
    // 配置中断引脚
    gpio_config_t io_conf = {};
    io_conf.intr_type = GPIO_INTR_NEGEDGE;
    io_conf.mode = GPIO_MODE_INPUT;
    io_conf.pin_bit_mask = (1ULL << _interruptPin);
    io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
    io_conf.pull_up_en = GPIO_PULLUP_ENABLE;
    
    esp_err_t ret = gpio_config(&io_conf);
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "配置中断引脚失败");
        return;
    }
    
    // 安装中断服务
    ret = gpio_install_isr_service(0);
    if (ret != ESP_OK && ret != ESP_ERR_INVALID_STATE) {
        ESP_LOGE(TAG, "安装中断服务失败");
        return;
    }
    
    // 添加中断处理程序
    ret = gpio_isr_handler_add((gpio_num_t)_interruptPin, interruptHandler, this);
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "添加中断处理程序失败");
        return;
    }
    
    // 初始化编码器引脚读取
    for (int i = 0; i < 8; i++) {
        if (encoderPins[i]) {
            digitalRead(i);
        }
    }
    
    ESP_LOGI(TAG, "中断配置完成，引脚: %d", _interruptPin);
}

void PCF8574::detachInterrupt() {
    if (!_usingInterrupt) {
        return;
    }
    
    esp_err_t ret = gpio_isr_handler_remove((gpio_num_t)_interruptPin);
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "移除中断处理程序失败");
        return;
    }
    
    ESP_LOGI(TAG, "中断已禁用");
}

// ==================== 状态检查 ====================

bool PCF8574::isOnline() {
    if (!_i2c) return false;
    return _i2c->probeDevice(_address);
}

// ==================== ESP32扩展功能 ====================

esp_err_t PCF8574::scanDevices(std::vector<uint8_t>& foundDevices) {
    if (!_i2c || !_i2c->isInitialized()) {
        ESP_LOGE(TAG, "I2C未初始化");
        return ESP_ERR_INVALID_STATE;
    }
    
    foundDevices.clear();
    
    ESP_LOGI(TAG, "开始扫描PCF8574设备");
    
    // 扫描PCF8574地址范围
    for (uint8_t addr = PCF8574_MIN_ADDRESS; addr <= PCF8574_MAX_ADDRESS; addr++) {
        if (_i2c->probeDevice(addr)) {
            foundDevices.push_back(addr);
            ESP_LOGI(TAG, "找到PCF8574设备: 0x%02X", addr);
        }
        vTaskDelay(pdMS_TO_TICKS(1));
    }
    
    // 扫描PCF8574A地址范围
    for (uint8_t addr = PCF8574A_MIN_ADDRESS; addr <= PCF8574A_MAX_ADDRESS; addr++) {
        if (_i2c->probeDevice(addr)) {
            foundDevices.push_back(addr);
            ESP_LOGI(TAG, "找到PCF8574A设备: 0x%02X", addr);
        }
        vTaskDelay(pdMS_TO_TICKS(1));
    }
    
    ESP_LOGI(TAG, "扫描完成，找到 %zu 个设备", foundDevices.size());
    return ESP_OK;
}

uint8_t PCF8574::readRaw() {
    if (!_deviceHandle) {
        ESP_LOGE(TAG, "设备句柄无效，请先调用begin()");
        transmissionStatus = 4;
        return 0;
    }
    
    uint8_t data;
    esp_err_t ret = _i2c->receive(_deviceHandle, &data, 1);
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "读取原始数据失败: %s", _i2c->errorToString(ret));
        transmissionStatus = 1;
        return 0;
    }
    
    transmissionStatus = 0;
    return data;
}

esp_err_t PCF8574::writeRaw(uint8_t value) {
    if (!_deviceHandle) {
        ESP_LOGE(TAG, "设备句柄无效，请先调用begin()");
        transmissionStatus = 4;
        return ESP_ERR_INVALID_STATE;
    }
    
    esp_err_t ret = _i2c->transmit(_deviceHandle, &value, 1);
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "写入原始数据失败: %s", _i2c->errorToString(ret));
        transmissionStatus = 1;
        return ret;
    }
    
    transmissionStatus = 0;
    return ESP_OK;
}

esp_err_t PCF8574::softReset() {
    if (!_deviceHandle) {
        ESP_LOGE(TAG, "设备句柄无效，请先调用begin()");
        transmissionStatus = 4;
        return ESP_ERR_INVALID_STATE;
    }
    
    // 重置所有状态
    writeMode = 0b00000000;
    writeModeUp = 0b00000000;
    readMode = 0b00000000;
    readModePullUp = 0b00000000;
    readModePullDown = 0b00000000;
    byteBuffered = 0b00000000;
    writeByteBuffered = 0b00000000;
    resetInitial = 0b00000000;
    initialBuffer = 0b00000000;
    encoderValues = 0b00000000;
    
    memset(encoderPins, false, sizeof(encoderPins));
    
    // 写入全高电平（PCF8574默认状态）
    uint8_t resetValue = 0xFF;
    esp_err_t ret = _i2c->transmit(_deviceHandle, &resetValue, 1);
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "软复位失败: %s", _i2c->errorToString(ret));
        transmissionStatus = 1;
        return ret;
    }
    
    transmissionStatus = 0;
    ESP_LOGI(TAG, "PCF8574软复位完成");
    return ESP_OK;
}

esp_err_t PCF8574::getPinInfo(uint8_t pin, bool& isOutput, bool& isHigh, bool& isPullup) {
    if (pin > 7) {
        ESP_LOGE(TAG, "无效引脚号: %d", pin);
        return ESP_ERR_INVALID_ARG;
    }
    
    uint8_t pinBit = bit(pin);
    
    isOutput = (writeMode & pinBit) != 0;
    isHigh = isOutput ? ((writeByteBuffered & pinBit) != 0) : ((byteBuffered & pinBit) != 0);
    isPullup = (readModePullUp & pinBit) != 0;
    
    return ESP_OK;
}

void PCF8574::setPinModes(uint8_t pinMask, uint8_t mode, uint8_t initialValue) {
    for (int pin = 0; pin < 8; pin++) {
        if (pinMask & bit(pin)) {
            pinMode(pin, mode, initialValue);
        }
    }
}

// ==================== 私有方法 ====================

esp_err_t PCF8574::readBuffer(bool force) {
    if (!_deviceHandle) {
        ESP_LOGE(TAG, "设备句柄无效，请先调用begin()");
        transmissionStatus = 4;
        return ESP_ERR_INVALID_STATE;
    }
    
    unsigned long currentMillis = millis();
    
    if (currentMillis > lastReadMillis + latency || _usingInterrupt || force) {
        uint8_t inputData;
        esp_err_t ret = _i2c->receive(_deviceHandle, &inputData, 1);
        if (ret != ESP_OK) {
            ESP_LOGE(TAG, "读取缓冲区失败: %s", _i2c->errorToString(ret));
            transmissionStatus = 1;
            return ret;
        }
        
        lastReadMillis = currentMillis;
        transmissionStatus = 0;
        
        // 检查是否有变化
        if ((inputData & readModePullDown) > 0 || (~inputData & readModePullUp) > 0) {
            byteBuffered = (byteBuffered & ~readMode) | (inputData & readMode);
        }
    }
    
    return ESP_OK;
}

void PCF8574::setVal(uint8_t pin, uint8_t value) {
    if (value == HIGH) {
        writeByteBuffered |= bit(pin);
        byteBuffered |= bit(pin);
    } else {
        writeByteBuffered &= ~bit(pin);
        byteBuffered &= ~bit(pin);
    }
}

void IRAM_ATTR PCF8574::interruptHandler(void* arg) {
    PCF8574* pcf = static_cast<PCF8574*>(arg);
    if (pcf) {
        pcf->handleInterrupt();
    }
}

void PCF8574::handleInterrupt() {
    if (_interruptFunction) {
        _interruptFunction();
    }
}

unsigned long PCF8574::millis() {
    return xTaskGetTickCount() * portTICK_PERIOD_MS;
}

bool PCF8574::isValidAddress(uint8_t address) {
    return ((address >= PCF8574_MIN_ADDRESS && address <= PCF8574_MAX_ADDRESS) ||
            (address >= PCF8574A_MIN_ADDRESS && address <= PCF8574A_MAX_ADDRESS));
} 